/*
 * Copyright 2008 Zoundry LLC, Inc.  All Rights Reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 */
package org.zoundry.gaedt.ui.editors;

import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.operations.IOperationHistory;
import org.eclipse.core.commands.operations.IUndoContext;
import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.ScrolledComposite;
import org.eclipse.swt.events.FocusAdapter;
import org.eclipse.swt.events.FocusEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.forms.IManagedForm;
import org.eclipse.ui.forms.editor.FormEditor;
import org.eclipse.ui.forms.editor.FormPage;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.ScrolledForm;
import org.eclipse.ui.forms.widgets.Section;
import org.zoundry.gaedt.core.models.application.ZGaeApplicationModel;
import org.zoundry.gaedt.core.util.ZGaeUtil;
import org.zoundry.gaedt.ui.editors.operations.IZGaeUndoableOperation;
import org.zoundry.gaedt.ui.editors.operations.ZGaeSetBeanPropertyOperation;
import org.zoundry.gaedt.ui.editors.operations.ZGaeUIUpdaterAdapter;


/**
 * The "Overview" page in the GAE Application form editor.
 *
 * TODO There is an initial focus problem - the section header gets the initial focus when the editor opens - the app name *should* get the focus
 * TODO We should have integrated help for some/all of the options on this page - perhaps just an F1 that links to the GAE help
 */
public class ZGaeApplicationOverviewPage extends FormPage implements IZGaeApplicationEditorPage
{
    /* Some widgets for the application meta data. */
    private Text mApplicationText;
    private Text mRuntimeText;
    private Text mVersionText;
    private Text mApiVersionText;

    /* Some widgets for the dev server settings. */
    private Button mDebugButton;
    private Text mPortText;
    private Text mAddressText;
    private Button mRequireIndexesButton;
    private Text mSMTPHostText;
    private Text mSMTPPortText;
    private Text mSMTPUserText;
    private Text mSMTPPasswordText;
    private Button mEnableSendmailButton;
    private Button mShowMailBodyButton;
    private Text mAuthDomainText;
    private Button mDebugImportsButton;
    private Button mDisableStaticCachingButton;

    //    /** Binding context for binding the model with UI widgets. */
    //    private DataBindingContext mBindingContext;

    /**
     * C'tor.
     *
     * @param aEditor
     * @param aId
     * @param aTitle
     */
    public ZGaeApplicationOverviewPage(FormEditor aEditor)
    {
        super(aEditor, ZGaeApplicationOverviewPage.class.getName(), "Overview");
        //        mBindingContext = new DataBindingContext();
    }

    /**
     * @see org.zoundry.gaedt.ui.editors.IZGaeApplicationEditorPage#getParentEditor()
     */
    public ZGaeApplicationEditor getParentEditor()
    {
        return ((ZGaeApplicationEditor) getEditor());
    }

    /**
     * @see org.eclipse.ui.forms.editor.FormPage#createFormContent(org.eclipse.ui.forms.IManagedForm)
     */
    @Override
    protected void createFormContent(IManagedForm aManagedForm)
    {
        ScrolledForm form = aManagedForm.getForm();
        FormToolkit toolkit = aManagedForm.getToolkit();
        form.setText(getTitle());
        toolkit.decorateFormHeading(form.getForm());
        form.getBody().setLayout(new FillLayout());

        Color color = getOrCreateForegroundColor(toolkit);

        // Create a layer for handling scrolling
        ScrolledComposite scrolledComposite = new ScrolledComposite(form.getBody(), SWT.H_SCROLL
                | SWT.V_SCROLL);

        Composite pageComposite = toolkit.createComposite(scrolledComposite, SWT.NONE);
        GridLayout layout = new GridLayout();
        layout.numColumns = 2;
        layout.makeColumnsEqualWidth = true;
        pageComposite.setLayout(layout);
        GridData data = new GridData(GridData.VERTICAL_ALIGN_FILL | GridData.HORIZONTAL_ALIGN_FILL);
        pageComposite.setLayoutData(data);
        // composite.setToolTipText(PAGE_DESCRIPTION);

        scrolledComposite.setContent(pageComposite);
        scrolledComposite.setMinSize(640, 300);
        scrolledComposite.setExpandHorizontal(true);
        scrolledComposite.setExpandVertical(true);

        // Create the app info group box
        ////////////////////////////////////
        Section appInfoSection = toolkit.createSection(pageComposite, Section.DESCRIPTION | Section.TITLE_BAR);
        appInfoSection.setText("Application Info");
        appInfoSection.setDescription("Information about the application (found in app.yaml).");
        data = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
        appInfoSection.setLayoutData(data);
        appInfoSection.setLayout(new GridLayout());

        Composite appInfoComposite = toolkit.createComposite(appInfoSection);
        layout = new GridLayout();
        layout.numColumns = 2;
        layout.makeColumnsEqualWidth = false;
        appInfoComposite.setLayout(layout);
        appInfoSection.setClient(appInfoComposite);

        // Application Name
        Label label = toolkit.createLabel(appInfoComposite, "Name:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mApplicationText = toolkit.createText(appInfoComposite, getModel().getName());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
        data.minimumWidth = 100;
        data.widthHint = 100;
        mApplicationText.setLayoutData(data);

        // Runtime
        label = toolkit.createLabel(appInfoComposite, "Type:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mRuntimeText = toolkit.createText(appInfoComposite, getModel().getRuntimeType());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
        data.minimumWidth = 100;
        data.widthHint = 100;
        mRuntimeText.setLayoutData(data);

        // Version
        label = toolkit.createLabel(appInfoComposite, "Version:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mVersionText = toolkit.createText(appInfoComposite, String.valueOf(getModel().getVersion()));
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
        data.minimumWidth = 40;
        data.widthHint = 40;
        mVersionText.setLayoutData(data);

        // API Version
        label = toolkit.createLabel(appInfoComposite, "API Version:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mApiVersionText = toolkit.createText(appInfoComposite, String.valueOf(getModel().getApiVersion()));
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
        data.minimumWidth = 40;
        data.widthHint = 40;
        mApiVersionText.setLayoutData(data);


        // Create the dev settings group box
        ///////////////////////////////////////
        Section devSettingsSection = toolkit.createSection(pageComposite, Section.DESCRIPTION | Section.TITLE_BAR);
        devSettingsSection.setText("Development Settings");
        devSettingsSection.setDescription("Settings that are applied at development/testing time (found in gae.yaml).");
        data = new GridData(GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING);
        devSettingsSection.setLayoutData(data);
        devSettingsSection.setLayout(new GridLayout());

        Composite devSettingsComposite = toolkit.createComposite(devSettingsSection);
        layout = new GridLayout();
        layout.numColumns = 2;
        layout.makeColumnsEqualWidth = false;
        devSettingsComposite.setLayout(layout);
        devSettingsSection.setClient(devSettingsComposite);

        // Debug Imports (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Debug:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mDebugButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mDebugButton.setToolTipText("Prints verbose debugging messages to the console while running.");
        mDebugButton.setSelection(getModel().isDevDebug());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mDebugButton.setLayoutData(data);

        // Dev Server Address
        label = toolkit.createLabel(devSettingsComposite, "Address:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mAddressText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevAddress()));
        mAddressText.setToolTipText("The host address to use for the server. You may need to set this to be able to access the development\nserver from another computer on your network. An address of 0.0.0.0 allows both localhost access and\nhostname access. Default is localhost.");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 120;
        data.widthHint = 120;
        mAddressText.setLayoutData(data);

        // Dev Server Port
        label = toolkit.createLabel(devSettingsComposite, "Port:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mPortText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevPort()));
        mPortText.setToolTipText("The port number to use for the server. Default is 8080.");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING);
        data.minimumWidth = 40;
        data.widthHint = 40;
        mPortText.setLayoutData(data);

        // Require Indexes (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Require Indexes:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mRequireIndexesButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mRequireIndexesButton.setToolTipText("Disables automatic generation of entries in the index.yaml file. Instead, when the application\nmakes a query that requires that its index be defined in the file and the index definition is\nnot found, an exception will be raised, similar to what would happen when running on App Engine.");
        mRequireIndexesButton.setSelection(getModel().isDevRequireIndexes());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mRequireIndexesButton.setLayoutData(data);

        // SMTP Host
        label = toolkit.createLabel(devSettingsComposite, "SMTP Host:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mSMTPHostText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevSMTPHost()));
        mSMTPHostText.setToolTipText("The hostname of the SMTP server to use for sending email messages.");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 120;
        data.widthHint = 120;
        mSMTPHostText.setLayoutData(data);

        // SMTP Port
        label = toolkit.createLabel(devSettingsComposite, "SMTP Port:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mSMTPPortText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevSMTPPort()));
        mSMTPPortText.setToolTipText("The port number of the SMTP server to use for sending email messages");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 40;
        data.widthHint = 40;
        mSMTPPortText.setLayoutData(data);

        // SMTP Username
        label = toolkit.createLabel(devSettingsComposite, "SMTP Username:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mSMTPUserText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevSMTPUser()));
        mSMTPUserText.setToolTipText("The username to use with the SMTP server for sending email messages.");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 180;
        data.widthHint = 180;
        mSMTPUserText.setLayoutData(data);

        // SMTP Password
        label = toolkit.createLabel(devSettingsComposite, "SMTP Password:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mSMTPPasswordText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevSMTPPassword()), SWT.SINGLE | SWT.PASSWORD);
        mSMTPPasswordText.setToolTipText("The password to use with the SMTP server for sending email messages.");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 180;
        data.widthHint = 180;
        mSMTPPasswordText.setLayoutData(data);

        // Enable Sendmail (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Enable Sendmail:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mEnableSendmailButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mEnableSendmailButton.setToolTipText("Enable sendmail when SMTP not configured.");
        mEnableSendmailButton.setSelection(getModel().isDevEnableSendmail());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mEnableSendmailButton.setLayoutData(data);

        // Show Mail Body (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Show Mail Body:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mShowMailBodyButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mShowMailBodyButton.setToolTipText("Log the body of emails in mail stub.");
        mShowMailBodyButton.setSelection(getModel().isDevShowMailBody());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mShowMailBodyButton.setLayoutData(data);

        // Auth Domain
        label = toolkit.createLabel(devSettingsComposite, "Auth Domain:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mAuthDomainText = toolkit.createText(devSettingsComposite, String.valueOf(getModel().getDevAuthDomain()));
        mAuthDomainText.setToolTipText("Authorization domain that this app runs in.  (Default gmail.com)");
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        data.minimumWidth = 180;
        data.widthHint = 180;
        mAuthDomainText.setLayoutData(data);

        // Debug Imports (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Debug Imports:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mDebugImportsButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mDebugImportsButton.setToolTipText("Prints debugging messages related to importing modules, including search paths and errors.");
        mDebugImportsButton.setSelection(getModel().isDevDebugImports());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mDebugImportsButton.setLayoutData(data);

        // Disable Static Caching (checkbox)
        label = toolkit.createLabel(devSettingsComposite, "Disable Static Caching:");
        label.setForeground(color);
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_END);
        label.setLayoutData(data);

        mDisableStaticCachingButton = toolkit.createButton(devSettingsComposite, "", SWT.CHECK);
        mDisableStaticCachingButton.setToolTipText("Never allow the browser to cache static files.  (Default enable if expiration set in app.yaml)");
        mDisableStaticCachingButton.setSelection(getModel().isDevDisableStaticCaching());
        data = new GridData(GridData.VERTICAL_ALIGN_CENTER | GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.GRAB_HORIZONTAL);
        mDisableStaticCachingButton.setLayoutData(data);

        // Set up listeners
        configureChangeListeners();
    }

    /**
     * Configure the various change listeners on the widgets.
     *
     * FIXME We should replace all of the change listeners with an implementation that uses the Eclipse data-binding framework
     *   Note: that will require some custom observable values, but would be highly re-usable
     */
    protected void configureChangeListeners()
    {
        //        IObservableValue appNameModelValue = PojoObservables.observeValue(getModel(), "name");
        //        IObservableValue appNameUIValue = SWTObservables.observeDelayedValue(250, SWTObservables.observeText(mAddressText, SWT.Modify));
        //        mBindingContext.bindValue(appNameUIValue, appNameModelValue, null, null);

        mApplicationText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "name",
                        mApplicationText.getText(), "Edit App Name", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mApplicationText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mRuntimeText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(),
                        "runtimeType", mRuntimeText.getText(), "Edit App Type", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mRuntimeText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mVersionText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "version",
                        Integer.parseInt(mVersionText.getText()), "Edit Version", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mVersionText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mApiVersionText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "apiVersion",
                        Integer.parseInt(mApiVersionText.getText()), "Edit API Version",
                        new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mApiVersionText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mDebugButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devDebug",
                        mDebugButton.getSelection(), "Edit Debug", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mDebugButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });

        mAddressText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devAddress",
                        mAddressText.getText(), "Edit Dev Address", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mAddressText.setText(aValue.toString());
                    }
                });
                executeOperation(operation);
            }
        });

        mPortText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devPort",
                        Integer.parseInt(mPortText.getText()), "Edit Dev Port", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mPortText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mRequireIndexesButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devRequireIndexes",
                        mRequireIndexesButton.getSelection(), "Edit Require Indexes", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mRequireIndexesButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });

        mSMTPHostText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devSMTPHost",
                        mSMTPHostText.getText(), "Edit SMTP Host", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mSMTPHostText.setText(aValue.toString());
                    }
                });
                executeOperation(operation);
            }
        });

        mSMTPPortText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devSMTPPort",
                        Integer.parseInt(mSMTPPortText.getText()), "Edit SMTP Port", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mSMTPPortText.setText(String.valueOf(aValue));
                    }
                });
                executeOperation(operation);
            }
        });

        mSMTPUserText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devSMTPUser",
                        mSMTPUserText.getText(), "Edit SMTP User", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mSMTPUserText.setText(aValue.toString());
                    }
                });
                executeOperation(operation);
            }
        });

        mSMTPPasswordText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devSMTPPassword",
                        mSMTPPasswordText.getText(), "Edit SMTP Password", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mSMTPPasswordText.setText(aValue.toString());
                    }
                });
                executeOperation(operation);
            }
        });

        mEnableSendmailButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devEnableSendmail",
                        mEnableSendmailButton.getSelection(), "Edit Enable Sendmail", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mEnableSendmailButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });

        mShowMailBodyButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devShowMailBody",
                        mShowMailBodyButton.getSelection(), "Edit Show Mail Body", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mShowMailBodyButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });

        mAuthDomainText.addFocusListener(new FocusAdapter()
        {
            @Override
            public void focusLost(FocusEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devAuthDomain",
                        mAuthDomainText.getText(), "Edit Auth Domain", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mAuthDomainText.setText(aValue.toString());
                    }
                });
                executeOperation(operation);
            }
        });

        mDebugImportsButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devDebugImports",
                        mDebugImportsButton.getSelection(), "Edit Debug Imports", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mDebugImportsButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });

        mDisableStaticCachingButton.addSelectionListener(new SelectionAdapter()
        {
            /**
             * @see org.eclipse.swt.events.SelectionAdapter#widgetSelected(org.eclipse.swt.events.SelectionEvent)
             */
            @Override
            public void widgetSelected(SelectionEvent aEvent)
            {
                IZGaeUndoableOperation operation = new ZGaeSetBeanPropertyOperation(getModel(), "devDisableStaticCaching",
                        mDisableStaticCachingButton.getSelection(), "Edit Disable Static Caching", new ZGaeUIUpdaterAdapter()
                {
                    @Override
                    protected void updateUI(Object aValue)
                    {
                        mDisableStaticCachingButton.setSelection((Boolean) aValue);
                    }
                });
                executeOperation(operation);
            }
        });
    }

    /**
     * Executes the given operation in the editor's operation history.
     *
     * @param aOperation
     */
    protected void executeOperation(IZGaeUndoableOperation aOperation)
    {
        if (!aOperation.shouldExecute())
            return;

        try
        {
            aOperation.addContext(getUndoContext());
            getOperationHistory().execute(aOperation, null, getEditor());
            getEditor().editorDirtyStateChanged();
        }
        catch (ExecutionException e)
        {
            ZGaeUtil.logError(e);
        }
    }

    /**
     * Gets an existing color from the toolkit, or creates a new one if one
     * does not already exist.
     *
     * @param aToolkit
     */
    protected Color getOrCreateForegroundColor(FormToolkit aToolkit)
    {
        Color color = aToolkit.getColors().getColor("gae_fg_color");
        if (color == null)
            color = aToolkit.getColors().createColor("gae_fg_color", 49, 105, 198);
        return color;
    }

    /**
     * Convenience method for getting the model.
     */
    protected ZGaeApplicationModel getModel()
    {
        return getParentEditor().getModel();
    }

    /**
     * Gets the undo context to use for operations.
     */
    protected IUndoContext getUndoContext()
    {
        return getParentEditor().getUndoContext();
    }

    /**
     * Gets the operation history.
     */
    protected IOperationHistory getOperationHistory()
    {
        return getSite().getWorkbenchWindow().getWorkbench().getOperationSupport().getOperationHistory();
    }
}
